package com.powernode.bank.utils;

import org.apache.ibatis.javassist.CannotCompileException;
import org.apache.ibatis.javassist.ClassPool;
import org.apache.ibatis.javassist.CtClass;
import org.apache.ibatis.javassist.CtMethod;
import org.apache.ibatis.javassist.expr.MethodCall;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.SqlSession;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 工具类：可以动态生成Dao的实现类
 */
public class GenerateDaoProxy {
    /**
     * 生成dao接口实现类，并且将实现类的对象创建出来并返回
     * @param daoInterface
     * @return
     */
    public static Object generate(SqlSession sqlSession,Class daoInterface){
        // 类池
        ClassPool pool = ClassPool.getDefault();
        // 制造类 (com.powernode.bank.dao.AccountDao --> com.powernode.bank.AccountDaoProxy)
        CtClass ctClass = pool.makeClass(daoInterface.getName()+"Proxy"); // 本质上是在内存中动态生成一个代理类
        // 制造接口
        CtClass ctInterface = pool.makeInterface(daoInterface.getName());
        // 实现接口
        ctClass.addInterface(ctInterface);
        // 实现接口中所有的方法
        Method[] methods = daoInterface.getDeclaredMethods();
        Arrays.stream(methods).forEach(method -> {
            // 将接口中的抽象方法method实现
            try {
                StringBuffer methodCode=new StringBuffer();
                //public Account selectByActno(String actno){};
                methodCode.append("public ");
                methodCode.append(method.getReturnType().getName());
                methodCode.append(" ");
                methodCode.append(method.getName());
                methodCode.append("(");
                Class<?>[] parameterTypes = method.getParameterTypes();
                for (int i = 0; i < parameterTypes.length; i++) {
                   methodCode.append(parameterTypes[i].getName()+" "+"arg"+i);
                    if (i != parameterTypes.length-1) {
                        methodCode.append(",");
                    }
                }
                methodCode.append(")");
                methodCode.append("{");
                // 需要方法体中的代码
                methodCode.append("org.apache.ibatis.session.SqlSession sqlSession=com.powernode.bank.utils.SqlSessionUtil.openSession();");
                // 需要知道是什么类型的标签
                // sql语句的id是框架使用者提供的，具有多变性
                // mybatis框架的开发者给出了一个规定，凡是使用GenerateDaoProxy机制的
                // sqlId都不能随便写，namespace必须是dao的全限定类名，id必须是dao中接口的方法名
                String sqlId=daoInterface.getName()+"."+method.getName();
                SqlCommandType sqlCommandType = sqlSession.getConfiguration().getMappedStatement(sqlId).getSqlCommandType();
                if(SqlCommandType.INSERT==sqlCommandType){

                }else if(SqlCommandType.DELETE==sqlCommandType){

                }else if(SqlCommandType.UPDATE==sqlCommandType){
                    methodCode.append("return sqlSession.update(\""+sqlId+"\", arg0);");
                }else if(SqlCommandType.SELECT==sqlCommandType){
                    String returnType = method.getReturnType().getName();
                    methodCode.append("return ("+returnType+")sqlSession.selectOne(\""+sqlId+"\", arg0);");
                }
                methodCode.append("}");
                CtMethod ctMethod = CtMethod.make(methodCode.toString(), ctClass);
                ctClass.addMethod(ctMethod);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        // 创建对象
        Object obj=null;
        try {
            Class<?> clazz = ctClass.toClass();
            obj = clazz.newInstance();
        } catch (CannotCompileException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }
}
